asynx.namespace("event");

asynx.event = {
	
	BACKSPACE: 8,
	TAB: 9,
	RETURN: 13,
	ENTER: 13,
	SHIFT: 16,
	CONTROL: 17,
	ESC: 27,
	SPACE: 32,
	PAGEUP: 33,
	PAGEDOWN: 34,
	END: 35,
	HOME: 36,
	LEFT: 37,
	UP: 38,
	RIGHT: 39,
	DOWN: 40,
	DELETE: 46,
	F5: 116,
	
	cache: [],
	
	isCached: function(element, fn, type, args) {
		
		
		var cached = false;
		
		if (!element._events) return;
		
		var evtCached = element._events[type];
		
		if (evtCached && evtCached[0] == fn 
			&& evtCached[1] == args) cached = true;
		
		return cached;
	},
	
	unloadCache: function() {
		var all = arguments[0];
		asynx.each(asynx.event.cache, function(evtCached, x) {
			if (evtCached) {
				var cachedElement = evtCached[0];
				var cachedType = evtCached[1];
				var cachedFn = evtCached[2];
					
				if (all == true || !cachedElement) {
					this.removeListener(cachedType, cachedElement, cachedFn);
					delete asynx.event.cache[x];
				}
			}
		}, this);
	},
	
	add: function(type, el) {
		var fn = arguments[2];
		var args = arguments[3];
		var nodes = asynx$(el);
		
		asynx.each(nodes, function(node) {
			this.addListener(type, node, fn, args);
		}, this);
	},
	
	addListener: function(type, el) {
		var nodes = asynx$(el);
		var fn = arguments[2];
		var args = arguments[3];
		var element = nodes;
		
		if (!fn || !type) return;
		
		var handle = function(e) {
			e = asynx.event.fix(e?e:event);
			if (asynx.isFunction(fn)) fn.apply(element, [e, args]);
			if (asynx.isIE)
				event.target = event.preventDefault = event.stopPropagation = null;
		};
		
		var isCached = this.isCached(element, fn, type, args);
		
		if (!isCached) {
			if (element.addEventListener)
				element.addEventListener(type, handle, false);
	
			else if (element.attachEvent && !isCached) { 
				element.attachEvent('on'+type, handle);
			}
		}
		
		// cache
		if (!isCached) {
			element._events = element._events||{};
			element._events[type] = element._events[type]||[];
			element._events[type].push([ fn, args, handle ]);
		}
		
		return element;
	},
	
	remove: function(type, el) {
		var fn = arguments[2];
		var nodes = asynx$(el);

		asynx.each(nodes, function(node) {
			this.removeListener(type, node, fn);
		}, this);
	},
	
	removeListener: function(type, el) {
		var fn = arguments[2];
		var element = asynx(el);
		if (!type) return;
		
		asynx.each(element._events[type], function(evtCached, x) {
			
			if (evtCached) {
				var cachedFn = evtCached[0];
				var cachedHandle = evtCached[2];
	
				var hasListener = !!(cachedFn == fn);
				
				var removeEvent = function() {
					if (element.removeEventListener)
						element.removeEventListener(type, cachedHandle, false);
					else
						element.detachEvent("on"+type, cachedHandle);
				};
				
				if (!fn) {
					removeEvent();
					element._events[type] = [];
					//delete asynx.event.cache[x];
				}
			
				if (fn && hasListener) {
					removeEvent();
					
					asynx.each(element._events[type], function(evtCache, x) {
						if (evtCache[0] == fn)
							element._events[type].splice(x,1);
					});
					
					//delete asynx.event.cache[x];
				}
			}
		});
			
	},
	
	click: function(el, fn) {
		var args = arguments[2];
		this.add('click', el, fn, args);
	},
	
	dblclick: function(el, fn) {
		var args = arguments[2];
		this.add('dblclick', el, fn, args);
	},
	
	blur: function(el, fn) {
		var args = arguments[2];
		this.add('blur', el, fn, args);
	},
	
	change: function(el, fn) {
		var args = arguments[2];
		this.add('change', el, fn, args);
	},
	
	focus: function(el, fn) {
		var args = arguments[2];
		this.add('focus', el, fn, args);
	},
	
	load: function(el, fn) {
		var args = arguments[2];
		this.add('load', el, fn, args);
	},
	
	keypress: function(el, fn) {
		var args = arguments[2];
		this.add('keypress', el, fn, args);
	},
	
	keyup: function(el, fn) {
		var args = arguments[2];
		this.add('keyup', el, fn, args);
	},
	
	keydown: function(el, fn) {
		var args = arguments[2];
		this.add('keydown', el, fn, args);
	},
	
	mouseover: function(el, fn) {
		var args = arguments[2];
		this.add('mouseover', el, fn, args);
	},
	
	mouseout: function(el, fn) {
		var args = arguments[2];
		this.add('mouseout', el, fn, args);
	},
	
	mousedown: function(el, fn) {
		var args = arguments[2];
		this.add('mousedown', el, fn, args);
	},
	
	mouseup: function(el, fn) {
		var args = arguments[2];
		this.add('mouseup', el, fn, args);
	},
	
	mousemove: function(el, fn) {
		var args = arguments[2];
		this.add('mousemove', el, fn, args);
	},
	
	onLoad: function(fn) {
		var args = arguments[1];
		this.add('load', window, fn, args);
	},
	
	onStopTyping: function(el, fn) {
		var uargs = arguments[2];
		var delay = arguments[3];
		var interval = null;
		
		this.keydown(el, function(e, args) {
			clearInterval(interval);
		});
		
		this.keyup(el, function(e, args) {
			clearInterval(interval);			
			interval = asynx.executeOn(function() {
				fn.apply(el, [e, uargs])
			}, delay?delay:1000);
		});
	},
	
	// opera doesnt call multiple times
	whileKeyPressed: function(el, keys) {
		el=asynx(el);
		var fn = arguments[2];
		var fargs = arguments[3];
		var fnEnd = arguments[4];
		var fargsEnd = arguments[5];
		var scope = arguments[6];
		asynx.event.key(el,{keys:keys,type:'keydown'},function(e, a) {
			if(fn)fn.apply(scope?scope:this,[e,fargs]);
		});
		asynx.event.key(el,{keys:keys,type:'keyup'},function(e, a) {
			if(fnEnd)fnEnd.apply(scope?scope:this,[e,fargsEnd]);
		});
	},

	wait: function(sObjName, fn) {
		var scope = arguments[2];
		var interval = asynx.executeOn(function() {
			if (asynx$(sObjName)) {
				asynx.executeOn(function() { fn.apply(scope); }, 50);
				clearInterval(interval);
			}
		}, 20, true, scope);
	},
	
	
	key: function(el, attrs, fn) {
		var element = asynx$(el);
		var type = attrs.type?attrs.type:"keyup";
		attrs.fn = arguments[2];
		attrs.args = arguments[3];
		var scope = arguments[4];
		
		this.addListener(type, element, function(e, args) {
			
			var shiftPressed = args.shift?true:false;
			var altPressed = args.alt ?true:false;
			var ctrlPressed = args.ctrl?true:false;
			var pressed = e.which;
			var validKey = false;
			
			asynx.each(args.keys, function(key) {
				if (!/\d+/.test(key))
					key = key.toString().charCodeAt(0);
				if (pressed == key)	validKey = true;
			});
			
			var meta = !!(e.ctrlKey == ctrlPressed &&
				 e.altKey == altPressed && e.shiftKey == shiftPressed);
			
			if (meta && validKey)
				args.fn.apply(scope?scope:element, [e, args.args]);	
						
		}, attrs);
	},
	
	
	getMouseXY: function(e) {
		var posx = 0;
		var posy = 0;
		if (!e) var e = window.event;
		if (e.pageX || e.pageY) 	{
			posx = e.pageX;
			posy = e.pageY;
		}
		else if (e.clientX || e.clientY) 	{
			posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
			posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
		}
		//return { x: posx, y:posy }; 
		return [posx, posy]; 
	},
	
	
	fix: function(event) {
		var original = event;
		event = asynx.extend({}, original);
		
		event.preventDefault = function() {
			if (original.preventDefault)
				return original.preventDefault();
			original.returnValue = false;
		};
		event.stopPropagation = function() {
			if (original.stopPropagation)
				return original.stopPropagation();
			original.cancelBubble = true;
		};
		
		if ( !event.target && event.srcElement )
			event.target = event.srcElement;
				
		if (asynx.isSafari && event.target.nodeType == 3)
			event.target = original.target.parentNode;

		if ( !event.relatedTarget && event.fromElement )
			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;

		if ( event.pageX == null && event.clientX != null ) {
			var e = document.documentElement, b = document.body;
			event.pageX = event.clientX + (e && e.scrollLeft || b.scrollLeft);
			event.pageY = event.clientY + (e && e.scrollTop || b.scrollTop);
		}
			
		if ( !event.which && (event.charCode || event.keyCode) )
			event.which = event.charCode || event.keyCode;
		
		if ( !event.metaKey && event.ctrlKey )
			event.metaKey = event.ctrlKey;

		if ( !event.which && event.button )
			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
			
		return event;
	}
	
};
